#include "SearchEngine.h"

static bool running = true;

SearchEngine::SearchEngine(int port) : indexer(this), thread(new ServerSocketImpl(port, this)) {
    initMap();
    initMapMethods();
    initPaths();
    thread.start();
}

void SearchEngine::initMap() {
    Catalogues* photoCatalog = new PhotoCatalog("catalogo_photo");
    Catalogues* docCatalog = new DocumentCatalog("catalogo_document");
    catalogues["audio/mpeg"] = new AudioCatalog("catalogo_audio");
    reverseCat["audio"] = catalogues["audio/mpeg"];
    catalogues["image/png"] = photoCatalog;
    catalogues["image/jpg"] = photoCatalog;
    reverseCat["photo"] = catalogues["image/jpg"];
    catalogues["text/code"] = new CodeCatalog("catalogo_code");
    reverseCat["source"] = catalogues["text/code"];
    catalogues["application/pdf"] = docCatalog;
    catalogues["document/txt"] = docCatalog;
    reverseCat["document"] = catalogues["document/txt"];
}

void SearchEngine::index() {
    //indexer.indexFs(paths);
}

std::string SearchEngine::stopServer(std::string) {
    writePaths();
    running = false;
    return std::string("Servidor Apagado correctamente");
}

void SearchEngine::reindex() {

}

void SearchEngine::processEvent() {

}

std::string SearchEngine::addDirectory(std::string dir) {
    std::string response;
    std::vector<std::string> aux;
    std::set<std::string>::iterator ite;
    std::map<std::string, Catalogues*>::iterator ite2 = catalogues.begin();
    ite = paths.find(dir);
    if (ite != paths.end()) {
        response = std::string("Ya se indexo el directorio");
        return response;
    }
    paths.insert(dir);
    aux.push_back(dir);
    this->indexer.indexFs(aux, 0, response);
    if (response.empty()) {
        response = "Ruta correctamente indexada";
        for (; ite2 != catalogues.end(); ite2++) {
            (ite2->second)->mostrarTerminosDoc();
        }
    }
    return response;
}

std::string SearchEngine::listDirectories(std::string dir) {
    std::string response;
    std::set<std::string>::iterator ite = paths.begin();
    for (; ite != paths.end(); ite++) {
        response += " " + (*ite) + "\n";
    }
    return response;
}

std::string SearchEngine::rankQuery(std::string query) {
    std::string returnValueStr("");
    RankPair rankPair = parseArgs(query);
    std::map<std::string, Catalogues*>::iterator ite = reverseCat.begin();
    std::vector<std::string> showResult;

    for (; ite != reverseCat.end(); ite++) {
        showResult = (ite->second)->rankingQuery(rankPair.first, rankPair.second);
        buildResponse(showResult, ite->first, returnValueStr);
    }
    if (returnValueStr.empty()) {
        returnValueStr += "\n     No Results      \n";
    }
    return returnValueStr;
}

std::string SearchEngine::rankQueryCatalog(std::string query) {
    std::string returnValueStr("");
    std::map<std::string, Catalogues*>::iterator ite;
    RankPair rankPair;
    std::vector<std::string> showResult;

    std::string catalogo = StaticUtils::split(query, ".")[0];
    query = query.substr(catalogo.size() + 1, query.size() - catalogo.size() - 1);
    ite = reverseCat.find(catalogo);
    if (ite == reverseCat.end()) {
        return (std::string("No existe el catalogo") + catalogo);
    }
    rankPair = parseArgs(query);
    showResult = (ite->second)->rankingQuery(rankPair.first, rankPair.second);
    buildResponse(showResult, ite->first, returnValueStr);
    if (returnValueStr.empty()) {
        returnValueStr += "\n     No Results      \n";
    }
    return returnValueStr;
}

std::string SearchEngine::wildCardQuery(std::string query) {
    std::string returnValueStr("");
    std::map<std::string, Catalogues*>::iterator ite = reverseCat.begin();
    std::vector<std::string> showResult;

    for (; ite != reverseCat.end(); ite++) {
        showResult = (ite->second)->getTerminosComodin(query);
        showResult = (ite->second)->puntualQuery(showResult);
        buildResponse(showResult, ite->first, returnValueStr);
    }
    if (returnValueStr.empty()) {
        returnValueStr += "\n     No Results      \n";
    }
    return returnValueStr;
}

std::string SearchEngine::wildCardQueryCatalogue(std::string query) {
    std::string returnValueStr("");
    std::map<std::string, Catalogues*>::iterator ite;
    std::vector<std::string> showResult;

    std::string catalogo = StaticUtils::split(query, ".")[0];
    query = query.substr(catalogo.size() + 1, query.size() - catalogo.size() - 1);
    ite = reverseCat.find(catalogo);
    if (ite == reverseCat.end()) {
        return (std::string("No existe el catalogo") + catalogo);
    }
    showResult = (ite->second)->getTerminosComodin(query);
    showResult = (ite->second)->puntualQuery(showResult);
    buildResponse(showResult, ite->first, returnValueStr);
    if (returnValueStr.empty()) {
        returnValueStr += "\n     No Results      \n";
    }
    return returnValueStr;
}

std::string SearchEngine::reindex(std::string par) {
    std::string response;
    std::map<std::string, Catalogues* >::iterator ite = reverseCat.begin();
    std::set<std::string>::iterator itepath = paths.begin();
    std::vector<std::string> pahtVec;

    for (; ite != reverseCat.end(); ite++) {
        (ite->second)->borrarCatalogo();
    }
    for (; itepath != paths.end(); itepath++) {
        pahtVec.push_back(*itepath);
    }
    indexer.indexFs(pahtVec, 0, response);
    return response;
}

void SearchEngine::buildResponse(std::vector<std::string> result, std::string catalogo, std::string& response) {
    std::vector<Return> returnValue;
    std::vector<Return>::iterator iteFirst;
    std::vector<std::string>::iterator itePair;
    std::vector<std::string> resultPair;
    if (!result.empty()) {
        returnValue.push_back(Return(((catalogo) + ":" + "\n"), result));
    }
    for (iteFirst = returnValue.begin(); iteFirst != returnValue.end(); iteFirst++) {
        response += (*iteFirst).first;
        resultPair = (*iteFirst).second;
        for (itePair = resultPair.begin(); itePair != resultPair.end(); itePair++) {
            response += (*itePair) + "\n";
        }
    }
}

std::string SearchEngine::removeDirectory(std::string dir) {
    std::string response;
    std::vector<std::string> aux;
    std::set<std::string>::iterator ite;
    ite = paths.find(dir);
    if (ite == paths.end()) {
        response = std::string("No existe el directorio a remover");
        return response;
    }
    paths.erase(ite);
    aux.push_back(dir);
    this->indexer.indexFs(aux, 2, response);
    if (response.empty())
        response = "Ruta correctamente borrada";
    return response;
}

Catalogues* SearchEngine::getCatalogue(std::string key) {
    std::map<std::string, Catalogues*>::iterator ite = catalogues.find(key);
    if (ite != catalogues.end()) {
        return (ite->second);
    }
    throw 0;
}

void SearchEngine::agregarRuta(std::string ruta) {
    paths.insert(ruta);
}

Method SearchEngine::getCommand(std::string prefix) throw (std::exception) {
    MapMethods::iterator ite;
    ite = mapMethods.find(prefix);
    if (ite != mapMethods.end()) {
        return ite->second;
    }
    throw std::exception();
}

RankPair SearchEngine::parseArgs(std::string args) {
    int modValue = 0;
    std::vector<std::string> terminos;
    std::vector<std::pair<unsigned, char> > relevancia;
    std::vector<unsigned> unsignedV;
    std::vector<char> charV;
    std::vector<unsigned>::iterator uV;
    std::vector<char>::iterator cV;
    int rel;
    char car;
    std::vector<std::string> vec = StaticUtils::split(args, ".");
    std::vector<std::string>::iterator ite = vec.begin();
    for (; ite != vec.end(); ite++) {
        if (modValue % 3 == 0) {
            terminos.push_back(*ite);
        } else if (modValue % 3 == 1) {
            rel = atoi((*ite).c_str());
            unsignedV.push_back(rel);
        } else if (modValue % 3 == 2) {
            car = *((*ite).c_str());
            charV.push_back(car);
        }
        modValue++;
    }
    uV = unsignedV.begin();
    cV = charV.begin();
    for (; uV != unsignedV.end(); uV++, cV++) {
        relevancia.push_back(std::pair<unsigned, char>((*uV), (*cV)));
    }
    return RankPair(terminos, relevancia);
}

void SearchEngine::initMapMethods() {
    mapMethods["pl"] = &SearchEngine::listDirectories;
    mapMethods["pa"] = &SearchEngine::addDirectory;
    mapMethods["pd"] = &SearchEngine::removeDirectory;
    mapMethods["st/r"] = &SearchEngine::rankQuery;
    mapMethods["sc/r"] = &SearchEngine::rankQueryCatalog;
    mapMethods["st/c"] = &SearchEngine::wildCardQuery;
    mapMethods["sc/c"] = &SearchEngine::wildCardQueryCatalogue;
    mapMethods["ri"] = &SearchEngine::reindex;
    mapMethods["stop"] = &SearchEngine::stopServer;
}

SearchEngine::~SearchEngine() {
    catalogues.clear();
    std::map<std::string, Catalogues*>::iterator ite = reverseCat.begin();
    for (; ite != reverseCat.end(); ite++) {
        if (ite->second != NULL)
            delete(ite->second);
    }
}

void SearchEngine::initPaths() {
    std::string path("./paths.sec");
    std::ifstream configFile;
    std::ofstream os;
    configFile.exceptions(std::ifstream::badbit);
    configFile.open(path.c_str(), std::ios::in);
    std::string values;
    if (!configFile.is_open()) {
        configFile.close();
        os.open(path.c_str(),ios_base::out);
        if (!os.is_open())
            throw string("No se pudo crear el archivo");
        os.clear();
        // Se activan las excepciones en el archivo.
        os.close();
    } else {
        getline(configFile, values);
        while (!configFile.eof()) {
            if (!values.empty()) {
                paths.insert(values);
                values.clear();
            }
            getline(configFile, values);
        }
        configFile.close();
    }
}

void SearchEngine::writePaths() {
    std::ofstream os("./paths.sec");
    std::set<std::string>::iterator ite=paths.begin();
    for(;ite!=paths.end();ite++) {
        os << *ite << std::endl;
    }
    os.close();
}

int main() {

    // Puerto del server
    SearchEngine engine(9898);
    //    int status;
    //    regex_t regex;
    //    regcomp(&regex,"^pri.*ce.a$",REG_EXTENDED|REG_ICASE);
    //    status = regexec(&regex,"princesa",(size_t)0, NULL, 0);
    //    regfree(&regex);
    //    std::vector<std::string> terminos;
    //    std::vector<unsigned> relevancia;
    //    terminos.push_back(std::string("princesa"));
    //    terminos.push_back(std::string("nombre"));
    //    relevancia.push_back(4);
    //    relevancia.push_back(1);
    //    engine.agregarRuta(std::string("/home/ariel/Desktop/SeverSocketApi"));
    //    engine.index();
    //    Catalogues* catalog = engine.getCatalogue("audio/mpeg");
    //    //Catalogues* catalog = engine.getCatalogue("image/png");
    //    catalog->mostrarTerminosDoc();
    while (running) {
        usleep(456666);
    }
    //catalog->rankingQuery(terminos,relevancia);

}
